#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seqInst> fin FIN_FICHIER
    sauterSymCour("debut");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }
    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        sauterSymCour(";");
    } while (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "si" || ls.getSymCour() == "tantque" || ls.getSymCour() == "repeter" || ls.getSymCour() == "pour" || ls.getSymCour() == "lire" || ls.getSymCour() == "ecrire");
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter>
    if (ls.getSymCour() == "<VARIABLE>") {
        return affectation();
    } else if (ls.getSymCour() == "si") {
        return instSi();
    } else if (ls.getSymCour() == "tantque") {
        return instTq();
    } else if (ls.getSymCour() == "repeter") {
        return instRepeter();
    } else if (ls.getSymCour() == "pour") {
        return instPour();
    } else if (ls.getSymCour() == "lire") {
        return instLire();
    } else if (ls.getSymCour() == "ecrire") {
        return instEcrire();
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi() {
    // <instSi>  ::= si ( <expBoolOu> ) <seqInst> { sinonsi ( <expBoolOu> ) <seqInst> } [ sinon <seqInst> ] finsi
    sauterSymCour("si");
    sauterSymCour("(");
    Noeud* condition = expBoolOu();
    sauterSymCour(")");
    Noeud* instructions = seqInst();
    NoeudInstSi* tete = new NoeudInstSi(condition, instructions);
    NoeudInstSi* courant = tete;
    while (ls.getSymCour() == "sinonsi") {
        sauterSymCour("sinonsi");
        sauterSymCour("(");
        Noeud* condition = expBoolOu();
        sauterSymCour(")");
        Noeud* instructions = seqInst();
        courant->setSinon(new NoeudInstSi(condition, instructions));
        courant = (NoeudInstSi*) courant->getSinon();
    }
    if (ls.getSymCour() == "sinon") {
        sauterSymCour("sinon");
        courant->setSinon(seqInst());
    }
    sauterSymCour("finsi");
    return tete;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instTq() {
    // <instTq>  ::= tantque ( <expBoolOu> ) <seqInst> fintantque
    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud* cond = expBoolOu();
    sauterSymCour(")");
    Noeud* inst = seqInst();
    sauterSymCour("fintantque");
    return new NoeudInstTantQue(cond, inst);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instRepeter() {
    // <instRepeter> ::= repeter <seqInst> jusqua ( <expBoolOu> )
    sauterSymCour("repeter");
    Noeud* inst = seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud* cond = expBoolOu();
    sauterSymCour(")");
    return new NoeudInstRepeter(cond, inst);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instPour() {
    // <instPour> ::= pour ( <affectation> ; <expBoolOu> ; <affectation> ) <seqInst> finpour
    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud* init = affectation();
    sauterSymCour(";");
    Noeud* cond = expBoolOu();
    sauterSymCour(";");
    Noeud* affe = affectation();
    sauterSymCour(")");
    Noeud* inst = seqInst();
    sauterSymCour("finpour");
    return new NoeudInstPour(init, cond, affe, inst);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instLire() {
    // <instLire> ::= lire ( <variable> )
    sauterSymCour("lire");
    sauterSymCour("(");
    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour(")");
    return new NoeudLire(var);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instEcrire() {
    // <instLire> ::= lire ( <variable> )
    Noeud* val;
    sauterSymCour("ecrire");
    sauterSymCour("(");
    if (ls.getSymCour() == "<CHAINE>") {
        val = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else {
        val = expression();
    }
    sauterSymCour(")");
    return new NoeudEcrire(val);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>
    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour("=");
    Noeud* exp = expression();
    return new NoeudAffectation(var, exp);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <chaine> | <terme> { <opAdd> <terme> }
    Noeud* term;
    if (ls.getSymCour() == "<CHAINE>") {
        sauterSymCour("<CHAINE>");
        term = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else {
        term = terme();
        while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
            Symbole operateur = opAdd();
            Noeud* termDroit = terme();
            term = new NoeudOperateurBinaire(operateur, term, termDroit);
        }
    }
    return term;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <entier> | <variable> | <opUnaire> <expBoolOu> | ( <expBoolOu> )
    Noeud* fact = NULL;

    if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>") {
        fact = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        Symbole op = opUnaire();
        fact = new NoeudOperateurUnaire(op, expBoolOu());
    } else if (ls.getSymCour() == "(") {
        ls.suivant();
        fact = expBoolOu();
        sauterSymCour(")");
    } else
        erreur("<facteur>");

    return fact;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBoolOu() {
    // <expBoolOu> ::= <expBoolEt> { <opBoolOu> <expBoolEt> }
    Noeud* r = expBoolEt();
    while (ls.getSymCour() == "ou") {
        r = new NoeudOperateurBooleen(opBoolOu(), r, expBoolEt());
    }
    return r;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBoolEt() {
    // <expBoolEt> ::= <relation> { <opBoolEt> <relation> }
    Noeud* r = relation();
    while (ls.getSymCour() == "et") {
        r = new NoeudOperateurBooleen(opBoolEt(), r, relation());
    }
    return r;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opBoolOu() {
    //   <opBoolOu> ::= ou
    if (ls.getSymCour() == "ou") {
        Symbole op = ls.getSymCour();
        ls.suivant();
        return op;
    } else
        erreur("<opBoolOu>");
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opBoolEt() {
    //   <opBoolEt> ::= et
    if (ls.getSymCour() == "et") {
        Symbole op = ls.getSymCour();
        ls.suivant();
        return op;
    } else
        erreur("<opBoolEt>");
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() {
    // <relation> ::= <expression> { <opRel> <expression> }
    Noeud* r = expression();
    while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == ">=" || ls.getSymCour() == "<=" || ls.getSymCour() == "<" || ls.getSymCour() == ">") {
        r = new NoeudRelation(opRel(), r, expression());
    }
    return r;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel() {
    // <opRel> ::= == | != | < | <= | > | >=
    if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == ">=" || ls.getSymCour() == "<=" || ls.getSymCour() == "<" || ls.getSymCour() == ">") {
        Symbole op = ls.getSymCour();
        ls.suivant();
        return op;
    } else
        erreur("<opRel>");
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire() {
    // <opUnaire> ::= - | non
    if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        Symbole op = ls.getSymCour();
        ls.suivant();
        return op;
    } else
        erreur("<opUnaire>");
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme()
// <terme> ::= <facteur> { <opMult> facteur> }
{
    Noeud* r = facteur();
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        r = new NoeudOperateurBinaire(opMult(), r, facteur());
    }
    return r;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd()
// <opAdd> ::=  + | -
{
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        Symbole op = ls.getSymCour();
        ls.suivant();
        return op;
    } else
        erreur("<opAdd>");
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult()
// <opMult> ::=  * | /
{
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        Symbole op = ls.getSymCour();
        ls.suivant();
        return op;
    } else
        erreur("<opMult>");
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    if (ls.getSymCour() != ch) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
        exit(0); // plus tard, on levera une exception
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    testerSymCour(ch);
    ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
            << ls.getColonne() << endl << "   Attendu : " << mess << endl
            << "   Trouve  : " << ls.getSymCour() << endl << endl;
    exit(0); // plus tard, on levera une exception
}
